package com.stone.oauth.controller;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.stone.oauth.service.AuthService;
import org.apache.oltu.oauth2.as.issuer.MD5Generator;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.oltu.oauth2.common.message.types.ParameterStyle;
import org.apache.oltu.oauth2.rs.request.OAuthAccessResourceRequest;
import org.apache.oltu.oauth2.rs.response.OAuthRSResponse;
import org.joda.time.DateTime;
import org.joda.time.format.ISODateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


/**
 * <p>Created with IntelliJ IDEA. </p>
 * <p>User: Stony </p>
 * <p>Date: 2016/7/7 </p>
 * <p>Time: 13:19 </p>
 * <p>Version: 1.0 </p>
 */
@Controller
@RequestMapping("")
public class AuthController {

    private static final Logger logger = LoggerFactory.getLogger(AuthController.class);

    @javax.annotation.Resource
    AuthService authService;

    /**
     * client_id=454545454&response_type=code&redirect_uri=http://xxx.com/path
     *
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     * @throws OAuthSystemException
     */
    @SuppressWarnings("unchecked")
    @RequestMapping(value = "/authorize", method = RequestMethod.GET)
    public Object authorization(HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws ServletException, IOException, OAuthSystemException {
        String redirectUri = request.getParameter(OAuth.OAUTH_REDIRECT_URI);
        try {
            OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
            //dynamically recognize an OAuth profile based on request characteristic (params,
            // method, content type etc.), perform validation
            OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request);
            String redirectURI = oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI);
            //校验客户端key
            if(authService.checkClient(oauthRequest.getClientId())){
                OAuthResponse oAuthResponse =
                        OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                                .setError(OAuthError.TokenResponse.INVALID_CLIENT)
                                .setErrorDescription("客户端未授权!!!")
                                .buildJSONMessage();
                HttpHeaders headers = new HttpHeaders();
                headers.add(OAuth.HeaderType.CONTENT_TYPE, "application/json;charset=UTF-8");
                return new ResponseEntity(oAuthResponse.getBody(), headers, HttpStatus.BAD_REQUEST);
            }
            //登录失败时跳转到登陆页面getClientId appKey
            if(!login(request)) {
                ModelAndView view = new ModelAndView();
                view.setViewName("oauth2login");
                view.addObject(OAuth.OAUTH_CLIENT_ID, oauthRequest.getClientId());
                view.addObject(OAuth.OAUTH_REDIRECT_URI, oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI));
                view.addObject(OAuth.OAUTH_RESPONSE_TYPE, oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE));
                logger.info("ModelMap = {}", view.getModelMap());
                return view;
            }
            String code = oauthIssuerImpl.authorizationCode();
            //build OAuth response
            OAuthResponse resp = OAuthASResponse
                    .authorizationResponse(request, HttpServletResponse.SC_FOUND)
                    .setCode(code)
                    .location(redirectURI)
                    .buildQueryMessage();
            authService.addCode(code, request.getParameter(OAuth.OAUTH_USERNAME));
            return new ModelAndView(new RedirectView(resp.getLocationUri()));
            //if something goes wrong

        } catch (OAuthProblemException ex) {
            final OAuthResponse resp = OAuthASResponse
                    .errorResponse(HttpServletResponse.SC_FOUND)
                    .error(ex)
                    .location(redirectUri)
                    .buildQueryMessage();

            return new ModelAndView(new RedirectView(resp.getLocationUri()));
        }
    }

    private boolean login(HttpServletRequest request) {
        if(OAuth.HttpMethod.POST.equalsIgnoreCase(request.getMethod())) {
            return false;
        }
        String username = request.getParameter(OAuth.OAUTH_USERNAME);
        String password = request.getParameter(OAuth.OAUTH_PASSWORD);

        return !(StringUtils.isEmpty(username) || StringUtils.isEmpty(password));
    }

    /**
     * Form 表单
     * <p>
     * client_id:1123123123
     * client_secret:123123123
     * grant_type:authorization_code
     * code:4dc9ce0ccfd728a0a55d57e3746658e5
     * redirect_uri:http://xxx.com/path
     * </p>
     *
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     * @throws OAuthSystemException
     */
    @SuppressWarnings("unchecked")
    @RequestMapping(value = "/access_token", method = RequestMethod.POST)
    public HttpEntity accessToken(HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws ServletException, IOException, OAuthSystemException {
        OAuthTokenRequest oauthRequest = null;

        OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());

        try {
            oauthRequest = new OAuthTokenRequest(request);

            //检查提交的客户端id是否正确 appKey
            if(authService.checkClient(oauthRequest.getClientId())){
                OAuthResponse oAuthResponse =
                        OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                                .setError(OAuthError.TokenResponse.INVALID_CLIENT)
                                .setErrorDescription("客户端未授权的呀！")
                                .buildJSONMessage();
                HttpHeaders headers = new HttpHeaders();
                headers.add(OAuth.HeaderType.CONTENT_TYPE, "application/json;charset=UTF-8");
                return new ResponseEntity(oAuthResponse.getBody(), headers, HttpStatus.BAD_REQUEST);
            }

            String authzCode = oauthRequest.getCode();
            if(authService.checkCode(authzCode)){
                OAuthResponse oAuthResponse =
                        OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                                .setError(OAuthError.TokenResponse.INVALID_CLIENT)
                                .setErrorDescription("客户端授权码异常")
                                .buildJSONMessage();
                HttpHeaders headers = new HttpHeaders();
                headers.add(OAuth.HeaderType.CONTENT_TYPE, "application/json;charset=UTF-8");
                return new ResponseEntity(oAuthResponse.getBody(),headers, HttpStatus.BAD_REQUEST);
            }
            // some code
            String accessToken = oauthIssuerImpl.accessToken();
            String refreshToken = oauthIssuerImpl.refreshToken();
            authService.addToken(accessToken, authService.getCodeValue(authzCode));
            authService.addToken(refreshToken, authService.getCodeValue(authzCode));
            // some code
            OAuthResponse r = OAuthASResponse
                    .tokenResponse(HttpServletResponse.SC_OK)
                    .setAccessToken(accessToken)
                    .setRefreshToken(refreshToken)
                    .setExpiresIn("36000000")
                    .buildJSONMessage();

            response.setStatus(r.getResponseStatus());
            return new ResponseEntity(r.getBody(), HttpStatus.OK);
            //if something goes wrong
        } catch (OAuthProblemException ex) {

            OAuthResponse r = OAuthResponse
                    .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                    .error(ex)
                    .buildJSONMessage();

            response.setStatus(r.getResponseStatus());
            return new ResponseEntity(r.getBody(), HttpStatus.UNAUTHORIZED);
        }
    }

    @SuppressWarnings("unchecked")
    @RequestMapping(value = "/resource", method = {RequestMethod.GET, RequestMethod.POST})
    public HttpEntity resource(HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws ServletException, IOException, OAuthSystemException {
        //构建OAuth资源请求
        OAuthAccessResourceRequest oauthRequest = null;
        try {
            if(OAuth.HttpMethod.POST.equalsIgnoreCase(request.getMethod())) {
                oauthRequest = new OAuthAccessResourceRequest(request, ParameterStyle.BODY);
            }else{
                oauthRequest = new OAuthAccessResourceRequest(request, ParameterStyle.QUERY);
            }

            //获取Access Token
            String accessToken = oauthRequest.getAccessToken();
            //验证Access Token
            if (authService.checkToken(accessToken)) {
                // 如果不存在/过期了，返回未验证错误，需重新验证
                OAuthResponse oauthResponse = OAuthRSResponse
                        .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                        .setRealm("客户端授权已过期")
                        .setError(OAuthError.ResourceResponse.INVALID_TOKEN)
                        .buildHeaderMessage();

                HttpHeaders headers = new HttpHeaders();
                headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
                return new ResponseEntity(headers, HttpStatus.UNAUTHORIZED);
            }

            Map<String,String> body = new HashMap<>();
            body.put("token", accessToken);
            body.put("user",authService.getTokenValue(accessToken));
            body.put("age","11");
            body.put("datetime", DateTime.now().toString(ISODateTimeFormat.dateTime()));
            return new ResponseEntity(body, HttpStatus.OK);
        } catch (OAuthProblemException e) {
            //build error response
            OAuthResponse oauthResponse = OAuthRSResponse
                    .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                    .setRealm("客户端授权校验错误")
                    .buildHeaderMessage();

            response.addDateHeader(OAuth.HeaderType.WWW_AUTHENTICATE, Long.parseLong(oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE)));
            HttpHeaders headers = new HttpHeaders();
            headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
            return new ResponseEntity(headers, HttpStatus.UNAUTHORIZED);
        }

    }

    @RequestMapping(value = "/photo/{id}", method = {RequestMethod.POST,RequestMethod.GET})
    public void photo(@PathVariable(value = "id") int id, javax.servlet.http.HttpServletResponse response) throws ServletException, IOException, OAuthSystemException, WriterException {
        logger.info("id = {}", id);
        MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
        Map hints = new HashMap();
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        String content = "http://www.stone.org/book/"+id;
        BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, 400, 400, hints);

        BufferedImage bufferedImage = toBufferedImage(bitMatrix);
        // 浏览器不缓存
        response.setDateHeader("expries", -1);
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");
        ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
    }

    private static final int BLACK = 0xFF000000;
    private static final int WHITE = 0xFFFFFFFF;
    public static BufferedImage toBufferedImage(BitMatrix matrix) {
        int width = matrix.getWidth();
        int height = matrix.getHeight();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
            }
        }
        return image;
    }
}
